package nait.cst.util;

import java.util.Scanner;

/**
 * This class facilitates user input by simplifying user validation for
 * both the data type and for encapsulating the use of the Scanner for
 * console input.
 * <p>
 * Usage: To use this class, simply call the static prompt method with your
 * message and then use the appropriate data conversion method. For example,
 * to get a String, simply write the following code.
 * <br>
 * {@code String name = prompt("Enter your name").toString();}
 * <br>
 * Alternatively, to get an int, you could write the following:
 * <br>
 * {@code int age = prompt("Enter your age").toInt();}
 * <p>
 * Note that the above examples are assuming the use of a static import
 * of the UserPrompt static methods. To import this static method, simply
 * include the following at the start of your file:
 * <br>
 * {@code import static data.UserPrompt.*;}
 * @author Dan Gilleland
 */
public class UserPrompt
{
    private static Scanner input = new Scanner(System.in);
    // String name = prompt("Enter your name").toString();
    // 

    private String data;
    private String message;

    /**
     * This constructor is for private, internal use only. It is used by
     * the prompt method to encapsulate the user's input and allow
     * conversion of that input to the desired data type.
     * @param data The user's input
     * @param message The original message from the prompt to the user
     */
    private UserPrompt(String data, String message)
    {
        this.data = data;
        this.message = message;
    }

    /**
     * Returns the results of a user prompt as an integer.
     * Note that if the user did not enter an integer for
     * the original prompt, the user will be re-prompted
     * for entering information in the correct format.
     * @return An int value from the user's input.
     */
    public int toInt()
    {
        int value = 0;
        try
        {
            value = Integer.parseInt(data);
        } catch (NumberFormatException ex)
        {
            System.err.println("\tData was not in the correct format. "
                    + "Please enter a whole number.");
            delay();
            value = prompt(message).toInt();
        }
        return value;
    }

    /**
     * Returns the results of a user prompt as a String.
     * Note that this method will return the entire line of text that the
     * user inputed (including any embedded spaces).
     * @return An String value from the user's input.
     */
    public String toString()
    {
        return data;
    }

    /**
     * Returns the results of a user prompt as a real number.
     * Note that if the user did not enter a number for
     * the original prompt, the user will be re-prompted
     * for entering information in the correct format.
     * @return An double value from the user's input.
     */
    public double toDouble()
    {
        double value = 0.0;
        try
        {
            value = Double.parseDouble(data);
        } catch (NumberFormatException ex)
        {
            System.err.println("\tData was not in the correct format. "
                    + "Please enter a real number.");
            delay();
            value = prompt(message).toDouble();
        }
        return value;
    }

    /**
     * Returns the results of a user prompt as a single character.
     * Note that this method will return onlyl the first character of
     * the user's input, even if they entered an entire line of text.
     * All text, other than the first character, is ignored.
     * @return An char value from the user's input.
     */
    public char toChar()
    {
        return data.charAt(0);
    }

    /**
     * Returns the results of a user prompt as a boolean.
     * This method interprets responses of "Yes", "Y", "True", or "T"
     * (ignoring upper and lower case) as true values. Any other
     * response by the user is regarded as a false value.
     * @return An boolean value from the user's input.
     */
    public boolean toBoolean()
    {
        boolean response = false;
        if (data.equalsIgnoreCase("Yes") || data.equalsIgnoreCase("Y")
                || data.equalsIgnoreCase("True") || data.equalsIgnoreCase("T"))
            response = true;
        return response;
    }

    /**
     * 
     * @param message
     * @return A UserPrompt object that can be used to "extract" the
     * user's input in the desired format. Use this with the various methods
     * of the UserPrompt class to get the desired format. Currently, the
     * UserPrompt performs conversions to int, double, char, boolean, and String.
     * <p>
     * If the user does not enter any text (or enters only spaces) the user is
     * re-prompted to enter the requested information.
     * Note also that the user's input is trimmed of leading and trailing spaces, to
     * prevent the entry of "empty" spaces and to ensure that any desired
     * numerical input is not misinterpreted due to leading or trailing spaces.
     */
    public static UserPrompt prompt(String message)
    {
        System.out.print(message + ": ");
        String response = input.nextLine().trim();
        while (response.isEmpty())
        {
            System.err.println("Invalid response: Please re-try");
            delay();
            System.out.print(message + ": ");
            response = input.nextLine().trim();
        }
        return new UserPrompt(response, message);
    }
    
    /**
     * This method is used internally to improve the displaying of error messages
     * to the user. The delay allows messages to the System.err object to be displayed
     * before another attempt at printing to the System.out object.
     */
    private static void delay()
    {
        int b = 0;
        for(int i = 0; i < 50000; i++)
            b += i;
    }
}
